//
// GPSMapEdit
// (c) Konstantin Galichsky (kg@geopainting.com), 2002-2007
//
// Current tool handling.
//

# include "StdAfx.h"
# include "Tool.h"
# include "Menu.h"
# include "Map.h"
# include "Settings.h"
# include "MapView.h"
# include "Wrappers.h"
# include "Globals.h"

HCURSOR g_hCursor;
HCURSOR g_hPermanentCursor;

CWaiter::CWaiter () {
	::SetCursor (g_hCursor = g_hWaitCursor);
}

void CWaiter::Stop () {
	::SetCursor (g_hCursor = g_hPermanentCursor);
}

///////////////////////////////////////////////

CTool * CTool::s_pFirst;

CTool::CTool (Tool_t _ToolId) :
	m_ToolId (_ToolId)
{
	m_pNext = CTool::s_pFirst;
	CTool::s_pFirst = this;
}

CTool * CTool::FindTool (Tool_t _ToolId) {
	if (_ToolId > toolMax)
		_ToolId = toolDefault;

	CTool * pTool;
	for (pTool = CTool::s_pFirst; pTool; pTool = pTool->m_pNext)
		if (pTool->m_ToolId == _ToolId)
			return pTool;

	for (pTool = CTool::s_pFirst; pTool; pTool = pTool->m_pNext)
		if (pTool->m_ToolId == toolDefault)
			return pTool;

	return NULL;
}

CTool * CTool::s_pTool;

bool CTool::_Init () {
	CTool::s_cXDrag = ::GetSystemMetrics (SM_CXDRAG);
	CTool::s_cYDrag = ::GetSystemMetrics (SM_CYDRAG);

	for (CTool * pTool = CTool::s_pFirst; pTool; pTool = pTool->m_pNext)
		if (! pTool->Init ())
			return false;

	CTool::SetCurrentTool (static_cast<Tool_t> (GetSetting ("Tool", (DWORD) toolDefault)));

	return true;
}

void CTool::_Destroy () {
	for (CTool * pTool = CTool::s_pFirst; pTool; pTool = pTool->m_pNext)
		pTool->Destroy ();
}

void CTool::_OnNewMap () {
	for (CTool * pTool = CTool::s_pFirst; pTool; pTool = pTool->m_pNext)
		pTool->OnNewMap ();
}

void CTool::_OnRemoveMap () {
	for (CTool * pTool = CTool::s_pFirst; pTool; pTool = pTool->m_pNext)
		pTool->OnRemoveMap ();
}

void CTool::SetCurrentTool (Tool_t _Tool) {
	CTool * const pTool = FindTool (_Tool);
	if (pTool == NULL)
		return;

	if (pTool == s_pTool) {
		// NOTE: clicking toolbar group button being already in pressed state will unpress it.
		// Make sure that the toolbar button will be still pressed.
		UpdateMenu ();

		s_pTool->OnSwitchSubTool ();
		return;
	}

	if (s_pTool)
		s_pTool->OnDeactivate ();

	s_pTool = pTool;

	SaveSetting ("Tool", (DWORD) _Tool);
	UpdateMenu ();

	s_pTool->OnActivate ();

	Redraw ();
}

Tool_t CTool::GetCurrentToolId () {
	return s_pTool ? s_pTool->m_ToolId : toolDefault;
}

size_t CTool::s_cXDrag,
       CTool::s_cYDrag;
int    CTool::s_iXDragStart,
       CTool::s_iYDragStart;
DWORD  CTool::s_dwTimeDragStart;
bool   CTool::m_bSuppressDrag = false;

bool   CTool::s_bDND = false;
long   CTool::s_lX_DND_Start;
long   CTool::s_lY_DND_Start;
DWORD  CTool::s_dwTime_DND_Start;
long   CTool::s_lX_DND;
long   CTool::s_lY_DND;

void CTool::SetPermanentCursor (HCURSOR _hcur) {
	g_hCursor = g_hPermanentCursor = _hcur;

	POINT p;
	::GetCursorPos (& p);
	if (::WindowFromPoint (p) == g_hMapWnd)
		::SetCursor (_hcur);
}

void CTool::StartDND (long _lX, long _lY) {
	if (CTool::s_bDND)
		return;

	CTool::s_bDND = true;
	CTool::s_dwTime_DND_Start = ::GetTickCount ();
	CTool::s_lX_DND_Start = _lX;
	CTool::s_lY_DND_Start = _lY;
	CTool::s_lX_DND = _lX;
	CTool::s_lY_DND = _lY;
	::SetCapture (g_hMapWnd);
}

void CTool::StopDND () {
	if (! CTool::s_bDND)
		return;

	CTool::s_bDND = false;
	::ReleaseCapture ();
}

void CTool::_OnLDown (int _x, int _y, DWORD _dwKeys) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}

	CTool::m_bSuppressDrag = true;
	CTool::s_iXDragStart = _x;
	CTool::s_iYDragStart = _y;
	CTool::s_dwTimeDragStart = ::GetTickCount ();

	if (s_pTool)
		s_pTool->OnLDown (_x, _y, _dwKeys);
}

void CTool::_OnLUp (int _x, int _y, DWORD _dwKeys) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}

	CTool::m_bSuppressDrag = false;
	if (s_pTool)
		s_pTool->OnLUp (_x, _y, _dwKeys);
}

void CTool::_OnMove (int _x, int _y, DWORD _dwKeys) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}

	if (CTool::m_bSuppressDrag) {
		if (
			size_t (::labs (_x - CTool::s_iXDragStart)) <= CTool::s_cXDrag &&
			size_t (::labs (_y - CTool::s_iYDragStart)) <= CTool::s_cYDrag &&
			::GetTickCount () - CTool::s_dwTimeDragStart <= 1000
		)
			return;

		CTool::m_bSuppressDrag = false;
	}

	if (CTool::s_bDND) {
		CTool::s_lX_DND = _x;
		CTool::s_lY_DND = _y;
	}

	if (s_pTool)
		s_pTool->OnMove (_x, _y, _dwKeys);
}

void CTool::_OnLDblClk (int _x, int _y, DWORD _dwKeys) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}
	if (s_pTool)
		s_pTool->OnLDblClk (_x, _y, _dwKeys);
}

void CTool::_OnRDown (int _x, int _y, DWORD _dwKeys) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}
	if (s_pTool)
		s_pTool->OnRDown (_x, _y, _dwKeys);
}

void CTool::_OnRUp (int _x, int _y, DWORD _dwKeys) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}
	if (s_pTool)
		s_pTool->OnRUp (_x, _y, _dwKeys);
}

void CTool::_OnPaint (HDC _hDC, const RECT & _rect) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}
	if (s_pTool)
		s_pTool->OnPaint (_hDC, _rect);
}

bool CTool::_OnCommand (WORD _wID) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return false;
	}
	return s_pTool ? s_pTool->OnCommand (_wID) : false;
}

void CTool::_OnCtxMenu (int _x, int _y) {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}

	if (s_pTool)
		s_pTool->OnCtxMenu (_x, _y);
}

void CTool::_OnEsc () {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}

	if (s_pTool)
		s_pTool->OnEsc ();
}

void CTool::_OnBack () {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}

	if (s_pTool)
		s_pTool->OnBack ();
}

void CTool::_OnEnter () {
	{
		CCritSec::CLock lock (g_cs);
		if (g_map.rectBound.Invalid ())
			return;
	}

	if (s_pTool)
		s_pTool->OnEnter ();
}
